/**
 * CatsDogsCanvas.java
 */

package rainingcatsdogs;

import java.io.IOException;
import java.util.Random;
import java.util.Vector;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.game.Sprite;
import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;

/**
 * The CatsDogsCanvas class to handle the main game.
 */
public class CatsDogsCanvas extends GameCanvas implements Runnable,
		CommandListener {

	/**
	 * set thread flag
	 */
	private volatile boolean running;

	/**
	 * A framerate limit to make the application smooth
	 */
	private final int FPSLIMIT = 20;

	/**
	 * Turret to display on screen
	 */
	private Turret turret;
	/**
	 * The terrain used in the game
	 */
	private Terrain terrain;

	/**
	 * List of enemies currently active in the game
	 */
	private Vector enemies;

	/**
	 * List of enemies that have been hit by some means and need to have their
	 * explode animation played
	 */
	private Vector destroyEnemies;

	/**
	 * List of bullets currently active in the game
	 */
	private Vector bullets;

	/**
	 * Random number generator
	 */
	private Random random;

	/**
	 * The likeliness of an enemy spawning every second
	 */
	private double enemySeed = 0.4;
	/**
	 * Last check for whether to spawn an enemy
	 */
	private long enemySpawnTime;

	/**
	 * Last check for whether to spawn a bonus enemy.
	 */
	private long bonusSpawnTime;

	/**
	 * Amount of time between bullets (ms)
	 */
	private long diffBulletTime = 200;
	/**
	 * Last time a bullet was fired
	 */
	private long lastBulletTime = 0;

	/**
	 * The last time a bomb was used
	 */
	private long lastBombTime = 0;
	/**
	 * The number of bombs the player currently has to use
	 */
	private int currentBombs;
	/**
	 * The number to reset the bombs to on the next level
	 */
	private int maxBombs;

	/**
	 * Current score
	 */
	private int currentScore;

	/**
	 * The amount of health that the enemies have.
	 */
	private int enemyHealth;
	/**
	 * number of enemies killed
	 */
	private int enemyKilled = 0;
	/**
	 * enemy speed
	 */
	private int enemySpeed = 2;

	/**
	 * The damage inflicted by the enemy on the turret.
	 */
	private int turretDamage;

	/**
	 * The damage inflicted by the enemy on the terrain.
	 */
	private int terrainDamage;

	/**
	 * The variable which holds the setting for turning music on or off.
	 */
	private boolean music = true;

	/**
	 * The variable which holds the setting for turning sound effects on or off.
	 */
	private boolean soundEffects = true;

	/**
	 * The variable which holds the setting for destroying the bonus enemy.
	 */
	private boolean bonusEnemyDestroyed = false;

	/**
	 * The variable which holds the number of lives.
	 */
	private int lives = 0;

	/**
	 * The main class that handles the main menu and running of the game.
	 */
	private CatsDogsMain main;

	/**
	 * The command which allows the player to exit the game.
	 */
	private Command exitCmd;
	/**
	 * Command to get to the help screen to show
	 */
	private Command helpCmd;
	/**
	 * Command to resume a paused game
	 */
	private Command resumeCmd;
	/**
	 * Command to pause a game
	 */
	private Command pauseCmd;

	/**
	 * The player object which allows sound to be played when the player shoots.
	 */
	private Player playerShoot;

	/**
	 * The player object which allows sound to be played when the enemy collides
	 * with the terrain or player.
	 */
	private Player explosion;

	/**
	 * The bomb image displayed next to the bomb counter.
	 */
	private Image bomb;

	/**
	 * The life image displayed next to the life counter.
	 */
	private Image life;

	/**
	 * Whether there is an active game running. This is used to be able to
	 * resume a paused game.
	 */
	private boolean activeGame;

	/**
	 * Levels for the game
	 */
	private Level gLevel;

	/**
	 * Whether or not the turret is dead/being destroyed
	 */
	private boolean turretDeath = false;

	/**
	 * Sprite to hold background image
	 */
	private Sprite backgroundImage;

	/**
	 * An array to hold the enemy images
	 */
	private Image[] enemyImages;

	/**
	 * The image object that holds the bonus enemy image.
	 */
	private Image bonusEnemyImage;

	/**
	 * The bonus enemy object.
	 */
	private Bonus bonusEnemy;

	/**
	 * Player for the level music.
	 */
	private Player levelMusic;

	/**
	 * Main game thread
	 */
	private Thread thread;

	/**
	 * Constructor for the game canvas for the application
	 * 
	 * @param main
	 *            main menu
	 * @param difficulty
	 *            setting for level difficulty
	 * @param music
	 *            turn music on or off
	 * @param soundEffects
	 *            turn sound effects on or off
	 */
	public CatsDogsCanvas(CatsDogsMain main, int difficulty, boolean music,
			boolean soundEffects) {
		super(true);
		this.main = main;
		this.music = music;
		this.soundEffects = soundEffects;

		try {
			bomb = Image.createImage("/bombupgrade.png");
			life = Image.createImage("/lifeupgrade.png");
		} catch (IOException e) {
		}

		// Create exit command to be able to be selected in the menu
		this.exitCmd = new Command("Exit", Command.ITEM, 5);
		this.helpCmd = new Command("Help", Command.ITEM, 2);
		this.resumeCmd = new Command("Resume", Command.BACK, 1);
		this.pauseCmd = new Command("Pause", Command.BACK, 1);
		this.addCommand(this.pauseCmd);
		this.addCommand(this.exitCmd);

		// Set this class to handle the event
		this.setCommandListener(this);

		// Create empty Vectors for the active enemies, enemies being destroyed
		// and bullets
		this.enemies = new Vector();
		this.bullets = new Vector();
		this.destroyEnemies = new Vector();

		// Create a random number generator used within the game
		this.random = new Random();

		if (this.soundEffects) {
			// sound effects
			try {
				playerShoot = Manager.createPlayer(getClass()
						.getResourceAsStream("/machgun.wav"), "audio/x-wav");
				explosion = Manager.createPlayer(getClass()
						.getResourceAsStream("/explosion.wav"), "audio/x-wav");
				playerShoot.prefetch();
				explosion.prefetch();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		this.activeGame = false;
		gLevel = new Level(this, difficulty);

		this.enemyImages = new Image[2];
		try {
			this.enemyImages[0] = Image.createImage("/enemy0.png");
			this.enemyImages[1] = Image.createImage("/enemy1.png");
		} catch (IOException e) {

		}

		try {
			this.bonusEnemyImage = Image.createImage("/enemy2.png");
		} catch (IOException e) {

		}

		this.thread = new Thread();
	}

	/**
	 * Add another bomb to the current count
	 */
	public void addBomb() {
		// TODO: Are bombs replenished on each level/life?
		this.maxBombs++;
	}

	/**
	 * Add another life to begin levels with
	 */
	public void addLife() {
		this.lives++;
	}

	/**
	 * Loops through each Enemy in the Vector of enemies to be destroyed and
	 * changes its frame to the next in sequence
	 * 
	 * 
	 */
	private void animateDestruction() {
		// for each enemy in destroyEnemies loop through and play a frame
		for (int i = 0; i < this.destroyEnemies.size(); i++) {

			Enemy e = (Enemy) this.destroyEnemies.elementAt(i);
			// The method will return true if we have played the last frame of
			// the explosion animation,
			// in this case we want to remove it from the list of enemies to
			// play animations for
			if (e.destroy()) {
				this.destroyEnemies.removeElementAt(i);
			}
		}

		if (this.bonusEnemyDestroyed) {
			if (this.bonusEnemy.destroy()) {
				this.bonusEnemy.setPosition(this.getWidth(), 0);
				this.bonusEnemyDestroyed = false;
			}
		}

	}

	/**
	 * Uses a bomb, which destroys all enemies currently on screen
	 */
	private void bomb() {
		if (this.currentBombs > 0
				&& System.currentTimeMillis() - this.lastBombTime > 1000) {
			for (int i = 0; i < this.enemies.size(); i++) {
				destroyEnemies.addElement(this.enemies.elementAt(i));
			}
			this.enemies.removeAllElements();
			this.currentBombs--;
			this.lastBombTime = System.currentTimeMillis();
			explosionSound();
		}
	}

	/**
	 * Checks the collisions between the following pairs of sprites: - Enemies
	 * and Terrain - Enemies and Turret - Enemies and Bullets
	 */
	private void checkCollisions() {
		// Loop through every enemy to see if any have collided with the terrain
		Enemy enemy;
		Bullet bullet;
		for (int i = 0; i < this.enemies.size(); i++) {
			enemy = (Enemy) this.enemies.elementAt(i);
			if (enemy.collidesWith(this.terrain, false)) {
				// If reducing the health leads to <= 0 health GAME OVER
				if (this.terrain.reduceHP(enemy.getTerrainDamage())) {
					if (lives > 0) {
						this.turretDeath = true;
						explosionSound();
						break;
					} else {
						this.gameover();
						explosionSound();
						break;
					}
				}

				// If there is a collision between enemy and terrain, destroy
				// enemy
				this.destroyEnemies.addElement(enemies.elementAt(i));
				this.enemies.removeElementAt(i);
				i--; // decrement i as all following elements have been moved
				// back by one
				explosionSound();
			} else if (enemy.collidesWith(this.turret, false)) {
				// If reducing the health leads to <= 0 health GAME OVER
				if (this.terrain.reduceHP(enemy.getTurretDamage())) {
					if (lives > 0) {
						this.turretDeath = true;
						explosionSound();
						break;
					} else {
						this.gameover();
						explosionSound();
						break;

					}
				}

				// If there is a collision between enemy and turret, destroy
				// enemy
				this.destroyEnemies.addElement(enemies.elementAt(i));
				this.enemies.removeElementAt(i);
				i--; // decrement i as all following elements have been moved
				// back by one
				explosionSound();
			} else {
				for (int x = 0; x < this.bullets.size(); x++) {
					bullet = (Bullet) this.bullets.elementAt(x);
					if (enemy.collidesWith(bullet, false)) {

						this.enemyKilled++;
						this.currentScore += 100;

						if (enemy.reduceHealth(bullet.getbulletDamage())) {
							this.destroyEnemies
									.addElement(enemies.elementAt(i));
							this.enemies.removeElementAt(i);
							i--;
						}
						this.bullets.removeElementAt(x);
						explosionSound();
					} else if(bullet.getY() > this.getHeight()) {
						this.bullets.removeElementAt(x);
						x--;
					}
				}
			}

			if (this.bonusEnemy != null && this.bonusEnemyDestroyed == false) {
				for (int x = 0; x < this.bullets.size(); x++) {
					bullet = (Bullet) this.bullets.elementAt(x);
					if (bullet.collidesWith(bonusEnemy, false)) {
						this.currentScore += 500;
						this.bonusEnemyDestroyed = true;
						explosionSound();
					}
				}
			}
		}
	}

	/**
	 * Handle the menu actions selected by the user
	 * 
	 * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command,
	 *      javax.microedition.lcdui.Displayable)
	 */
	public void commandAction(Command c, Displayable arg1) {
		if (c == this.exitCmd) {
			this.stop();
			this.main.returnMainMenu();
		} else if (c == this.helpCmd) {
			this.main.helpAlert(this);
		} else if (c == this.resumeCmd) {
			this.removeCommand(this.resumeCmd);
			this.removeCommand(this.helpCmd);
			this.addCommand(this.pauseCmd);
			this.resume();
		} else if (c == this.pauseCmd) {
			this.pause();
		}
	}

	/**
	 * Create the initial turret object to be drawn on the screen
	 * 
	 * @param fileName
	 *            The filename of the image to use as the turret
	 * @param width
	 *            The width of the turret image
	 * @param height
	 *            The height of the turret image
	 * @return The newly constructed turret object
	 */
	private Turret createPlayer(String fileName, int width, int height) {
		// Get the turret image
		Image tempImage = null;
		try {
			tempImage = Image.createImage(fileName);
		} catch (Exception ex) {
			return null;
		}

		// Construct and return the new Turret object
		return new Turret(10, tempImage, width, height, this.getWidth() / 2,
				this.getHeight() - 25);
	}

	/**
	 * sound for explosion
	 */
	public void explosionSound() {
		if (this.soundEffects == true) {
			if (this.explosion.getState() != Player.STARTED) {
				try {
					explosion.start();
				} catch (MediaException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * Game over. Stop game and leave
	 */
	private void gameover() {
		this.stop();
		this.main.gameOver();
		this.activeGame = false;
	}

	/**
	 * To get the score
	 * 
	 * @return current score
	 */
	public int getCurrentScore() {
		return currentScore;
	}

	/**
	 * Returns the number of enemies that have been killed
	 * @return number of enemies killed
	 */
	public int getEnemyKilled() {
		return enemyKilled;
	}

	/**
	 * Get the seed value that determines how often enemies fall
	 * @return The enemy seed
	 */
	public double getEnemySeed() {
		return this.enemySeed;
	}

	/**
	 * Return the enemy falling speed
	 * @return enemy speed
	 */
	public int getEnemySpeed() {
		return enemySpeed;
	}

	/**
	 * Check if keys have been pressed and move the turret as required
	 * 
	 * This function just updates the position of the turret. The turret is not
	 * redrawn until later in the main game loop.
	 */
	private void handleKey() {
		int keyState = getKeyStates();

		if ((keyState & GAME_A_PRESSED) != 0) {
			this.bomb();
		}

		if ((keyState & RIGHT_PRESSED) != 0) {
			// Move the turret to the right
			turret.move(true, false, this.getHeight(), this.getWidth());
		} else if ((keyState & LEFT_PRESSED) != 0) {
			// Move the turret to the left
			turret.move(false, false, this.getHeight(), this.getWidth());
		}

		if ((keyState & FIRE_PRESSED) != 0
				&& System.currentTimeMillis() - this.lastBulletTime > this.diffBulletTime) {
			this.lastBulletTime = System.currentTimeMillis();
			Bullet newBullet = this.turret.shoot();
			this.bullets.addElement(newBullet);
			shootSound();
		}

	}

	/**
	 * Find out whether the application has loaded the vital resources
	 * 
	 * @return Whether the class has loaded successfully
	 */
	public boolean isLoaded() {
		for (int i = 0; i < this.enemyImages.length; i++) {
			if (this.enemyImages[i] == null) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Return whether the main thread is running
	 * @return whether the main thread is running at this time
	 */
	public boolean isRunning() {
		return this.running;
	}

	/**
	 * Pause the game, updating the Display commands
	 */
	public void pause() {
		this.addCommand(this.helpCmd);
		this.removeCommand(this.pauseCmd);
		this.addCommand(this.resumeCmd);

		if (this.music == true) {
			try {
				this.levelMusic.stop();
				this.levelMusic.deallocate();
			} catch (MediaException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		this.stop();
	}

	/**
	 * Acquires resources for when starting the game or resuming from a pause
	 */
	public void prefetchResources() {
		try {
			this.playerShoot.prefetch();
			this.explosion.prefetch();
		} catch (MediaException e) {
			// Ignore sound error. Not of high importance to user
			e.printStackTrace();
		}
	}

	/**
	 * Method to create an enemy if the enemy seed is more than a generated
	 * random number
	 */
	private void randomCreateEnemy() {
		// Update the time that there was a possible enemy spawned, to create a
		// delay until the next
		this.enemySpawnTime = System.currentTimeMillis();

		// If random number decides to spawn enemy, or if there are no enemies
		// TODO: Have a max on screen at any one time (level dependent?)
		if (this.random.nextDouble() < this.enemySeed
				|| this.enemies.size() < 1) {

			// Get a random enemy image
			// TODO: Add more images to test random selection of images
			Image image = this.enemyImages[this.random.nextInt(2)];

			// Create a new enemy object based on the retrieved image
			Enemy enemy = new Enemy(5, image, 32, image.getHeight(),
					this.enemyHealth, this.terrainDamage, this.turretDamage);

			// Random location somewhere along the screen
			int newXPos = (int) ((this.getWidth() - 32) * this.random
					.nextDouble());

			// initial location is 3 pixels in
			enemy.setPosition(newXPos, (image.getHeight() * -1) + 3);

			int speed = (this.enemySpeed * (this.getHeight() / 130));
			if (speed == 0) {
				speed = 1;
			}
			enemy.setYStep(speed); // speed of enemy falling (pixels
			// per iteration)

			// Add the enemy to the main list of enemies to draw to the canvas
			this.enemies.addElement(enemy);
		}
	}

	/**
	 * Releases resources for when a pauseEvent has been called on the main
	 * MIDLet
	 */
	public void releaseResources() {
		this.playerShoot.deallocate();
		this.explosion.deallocate();
	}

	/**
	 * Update locations of bullets and enemies, and then draw bullets, enemies,
	 * turret, and terrain
	 * 
	 * @param g
	 *            The graphics object to draw to
	 */
	private void render(Graphics g) {
		if (this.backgroundImage != null) {
			this.backgroundImage.paint(g);
		}

		// Paint terrain onto the canvas
		terrain.paint(g);

		// Paint turret onto the canvas
		turret.paint(g);

		Bullet bullet;
		for (int i = 0; i < this.bullets.size(); i++) {
			bullet = (Bullet) this.bullets.elementAt(i);
			bullet.move(false, true, this.getHeight(), this.getWidth());
			if (bullet.getY() > 0) {
				bullet.paint(g);
			} else {
				this.bullets.removeElementAt(i);
				i--;
			}
		}

		// Update position of each enemy by one step (yStep) and paint to canvas
		Enemy enemy;
		for (int i = 0; i < this.enemies.size(); i++) {
			enemy = (Enemy) this.enemies.elementAt(i);
			enemy.move(false, false, this.getHeight(), this.getWidth());
			enemy.paint(g);
		}
		// draw the exploding enemies onto the screen
		for (int i = 0; i < this.destroyEnemies.size(); i++) {
			enemy = (Enemy) this.destroyEnemies.elementAt(i);
			enemy.paint(g);
		}

		if (this.bonusEnemy != null) {
			this.bonusEnemy.paint(g);
		}

		showStatus(g);

		// Flush the graphics object to the screen
		this.flushGraphics();
	}

	/**
	 * Resets the game when a life has been lost or player advances a level.
	 */
	public void reset() {
		turret.setPosition(this.getWidth() / 2, this.getHeight() - 32);
		this.terrain.resetHP();
		this.enemies.removeAllElements();
		this.bullets.removeAllElements();
		this.destroyEnemies.removeAllElements();
		this.bonusEnemy = null;
		this.bonusEnemyDestroyed = false;
		this.bonusSpawnTime = System.currentTimeMillis()
				+ this.random.nextInt(20) * 1000;
		
		try {
			this.backgroundImage = new Sprite(Image.createImage("/level"
					+ this.gLevel.getCurrLevel() + ".png"));
		} catch (IOException e) {
			// Ignore background if unable to be created
		}

		// Reset number of bombs
		this.currentBombs = this.maxBombs;
		//System.gc();
	}

	/**
	 * Resume a previously started game. Will revert to a new game if no
	 * previous game exists
	 */
	public void resume() {
		if (!this.activeGame) {
			this.start();
		} else {
			if (!this.thread.isAlive()) {
				if (this.music == true) {
					try {
						this.levelMusic.setLoopCount(-1);
						this.levelMusic.start();
					} catch (MediaException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				this.running = true;
				this.thread = new Thread(this);
				this.thread.start();
			}
		}
	}

	/**
	 * Main game-loop, started using a new thread
	 * 
	 * Each iteration of the main loop has to perform the following action: -
	 * Move the turret based on the captured key presses - Check the collisions
	 * between falling enemies and the other on screen elements - Create falling
	 * enemies at random times - Clear the canvas of existing content - Repaint
	 * all the objects on the canvas
	 * 
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		// get the graphics to render on the canvas
		Graphics g = getGraphics();

		// The work done in the main loop could take different amounts of time
		// on each iteration. So we need to avoid this by taking how long the
		// iteration away from the time to sleep between frames
		long startTime, sleepTime;
		int baseDelay = 1000 / this.FPSLIMIT; // calculate delay between frame
		// updates (ms)

		while (this.running == true) {
			// The time that the iteration started
			startTime = System.currentTimeMillis();

			if (!(this.turretDeath)) {
				this.handleKey();
				// Check for collisions between Sprites
				this.checkCollisions();
				// Only potentially create enemies every 1000ms
				if ((System.currentTimeMillis() - this.enemySpawnTime) >= 1000) {
					this.randomCreateEnemy();
				}
			} else {
				for (int i = 0; i < this.enemies.size(); i++) {
					destroyEnemies.addElement(this.enemies.elementAt(i));
				}
				this.enemies.removeAllElements();
				if (this.turret.destroy()) {
					this.reset();
					lives--;
					this.turret.setFrame(0);
					this.turret.setFramesLeft(10);
					this.turretDeath = false;
				}

			}
			// Loop through and play a frame for each of the enemies currently
			// being destroyed
			this.animateDestruction();

			if (System.currentTimeMillis() > bonusSpawnTime) {
				if (this.bonusEnemy == null) {
					this.bonusEnemy = new Bonus(5, bonusEnemyImage, 32, 32, 1);
					this.bonusEnemy.setPosition(-32, this.getHeight() / 4);
				} else if (this.bonusEnemy.getX() < this.getWidth() + 32) {
					this.bonusEnemy.move(6, 0);
				}
			}
			// clear screen
			g.setColor(0xFF0FFF);
			g.fillRect(0, 0, getWidth(), getHeight());

			// Write all Sprites to screen and flush graphics to phone
			this.render(g);

			if (gLevel.levelMove()) {
				this.stop();
				this.main.upgradeScreen();
				return;
			}

			try {
				// Calculate the time to sleep, taking into account how long the
				// current frame took to construct
				sleepTime = baseDelay
						- (System.currentTimeMillis() - startTime);
				if (sleepTime > 0) {
					Thread.sleep(sleepTime);
				}

			} catch (InterruptedException ie) {
				this.pause();
			}
			System.gc();
		}
	}

	/**
	 * Set the health enemies have
	 * @param enemyHealth
	 */
	public void setEnemyHealth(int enemyHealth) {
		this.enemyHealth = enemyHealth;
	}

	/**
	 * Set the speed
	 * 
	 * @param enemySeed
	 */
	public void setEnemySeed(double enemySeed) {
		this.enemySeed = enemySeed;
	}

	/**
	 * Sets the falling speed of the animes
	 * @param enemySpeed
	 *            set the falling enemy speed
	 */
	public void setEnemySpeed(int enemySpeed) {
		this.enemySpeed = enemySpeed;
	}

	/**
	 * Sets the number of lives the user has
	 * @param lives
	 */
	public void setLives(int lives) {
		this.lives = lives;
	}

	/**
	 * Sets the maximum number of bombs the user has
	 * @param maxBombs
	 */
	public void setMaxBombs(int maxBombs) {
		this.maxBombs = maxBombs;
	}

	/**
	 * Sets the damage the terrain takes
	 * @param terrainDamage
	 */
	public void setTerrainDamage(int terrainDamage) {
		this.terrainDamage = terrainDamage;
	}

	/**
	 * Sets the damage turret takes
	 * @param turretDamage
	 */
	public void setTurretDamage(int turretDamage) {
		this.turretDamage = turretDamage;
	}

	/**
	 * sound for the bullet
	 */
	public void shootSound() {
		if (this.soundEffects == true) {
			if (this.playerShoot.getState() != Player.STARTED) {
				try {
					playerShoot.start();
				} catch (MediaException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * show the energy for the turret and terrain
	 * @param g 
	 * 
	 */
	private void showStatus(Graphics g) {
		// Show the current health and score at the top of the screen
		//Graphics g = getGraphics();
		g.setColor(0x73b2e7);
		g.fillRect(0, 0, this.getWidth(), 18);
		g.setColor(0x0f0f0f);
		g.drawString(
				"HP:" 
						+ ((this.terrain.getHitPoints() < 0) ? 0 : this.terrain
								.getHitPoints()), 1, 2, 0);
		g.drawString("Points:" + this.currentScore, 46, 2, 0);
		g.drawImage(life, this.getWidth() - 29, 1, 0);
		g.drawString("" + this.lives, this.getWidth() - 10, 2, 0);
		g.drawImage(bomb, this.getWidth() - 57, 1, 0);
		g.drawString("" + this.currentBombs, this.getWidth() - 41, 2, 0);
	}

	/**
	 * Starts the game thread, constructing the main turret and terrain objects,
	 * and then starting the game thread
	 */
	public void start() {
		// Construct a dynamic image based on screen width and height for
		// the Terrain
		Image terrainImage = Image.createImage(this.getWidth(), 35);
		Graphics terrainGraphics = terrainImage.getGraphics();
		terrainGraphics.setColor(0xc3997d);
		terrainGraphics.fillRect(0, 0, this.getWidth(), 15);

		// Create the terrain object and position at the bottom of the screen
		this.terrain = new Terrain(terrainImage);
		this.terrain.setPosition(0, this.getHeight() - 15);

		// Create the turret object and position in the centre-bottom of the
		// screen
		turret = createPlayer("/turret2.png", 32, 32);
		turret.setPosition(this.getWidth() / 2, this.getHeight() - 32);

		int speed = this.getWidth() / 50;
		turret.setXStep(speed);

		this.currentBombs = this.maxBombs;

		this.activeGame = true;

		try {
			this.backgroundImage = new Sprite(Image.createImage("/level"
					+ this.gLevel.getCurrLevel() + ".png"));

		} catch (IOException e) {
			// Ignore background if unable to be created
		}

		this.bonusSpawnTime = System.currentTimeMillis()
				+ this.random.nextInt(20) * 1000;

		if (this.music == true) {
			try {
				this.levelMusic = Manager
						.createPlayer(
								getClass().getResourceAsStream(
										"/level_sunny.wav"), "audio/x-wav");
				this.levelMusic.setLoopCount(-1);
				this.levelMusic.start();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (MediaException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		// Start the main game thread running
		if (!this.thread.isAlive()) {
			this.running = true;
			this.thread = new Thread(this);
			this.thread.start();
		}
	}

	/**
	 * Stops the game thread. (Can be resumed later)
	 */
	public void stop() {
		if(this.music == true) {
			try {
				this.levelMusic.stop();
			} catch (MediaException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.running = false;
	}

	/**
	 * Updates the music player for the level with the correct music for that
	 * level.
	 * 
	 * @param fileName
	 */
	public void updateLevelMusic(String fileName) {
		try {
			this.levelMusic.stop();
			this.levelMusic = Manager.createPlayer(getClass()
					.getResourceAsStream(fileName), "audio/x-wav");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MediaException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Increase the bullet speed by 4 pixels per frame
	 */
	public void upgradeBullets() {
		this.turret.increaseBulletSpeed(2);
	}

	/**
	 * Add 2 pixels per frame to the speed of the turret
	 */
	public void upgradeTurret() {
		this.turret.setXStep(this.turret.getXStep() + 1);
	}
}
